package com.yiwenup.sorting.base;

import com.yiwenup.sorting._07_shell.ShellSort;
import com.yiwenup.sorting._08_couting.CountingSortV1;
import com.yiwenup.sorting._08_couting.CountingSortV2;
import com.yiwenup.sorting._09_radix.RadixSort;

import java.text.DecimalFormat;

/**
 * 排序基类
 **/
@SuppressWarnings("unchecked")
public abstract class Sort<E extends Comparable<E>> implements Comparable<Sort<E>> {

    protected E[] array;
    private long cmpCount;
    private long swapCount;
    private long time;
    private final DecimalFormat fmt = new DecimalFormat("#.00");

    public void sort(E[] array) {
        if (array == null || array.length < 2) return;
        this.array = array;
        long begin = System.currentTimeMillis();
        work();
        time = System.currentTimeMillis() - begin;
    }

    protected abstract void work();

    /**
     * =0：array[idx1] = array[idx2]
     * <0：array[idx1] < array[idx2]
     * >0：array[idx1] > array[idx2]
     */
    protected int cmp(int idx1, int idx2) {
        cmpCount++;
        return array[idx1].compareTo(array[idx2]);
    }

    /**
     * =0：e1 = e2
     * <0：e1 < e2
     * >0：e1 > e2
     */
    protected int cmp(E e1, E e2) {
        cmpCount++;
        return e1.compareTo(e2);
    }

    protected void swap(int idx1, int idx2) {
        swapCount++;
        E tmp = array[idx1];
        array[idx1] = array[idx2];
        array[idx2] = tmp;
    }

    @Override
    public int compareTo(Sort<E> o) {
        return (int) (this.time - o.time);
    }

    @Override
    public String toString() {
        String timeStr = "耗时：" + (time / 1000.0) + "s(" + time + "ms)";
        String compareCountStr = "比较：" + numberString(cmpCount);
        String swapCountStr = "交换：" + numberString(swapCount);
        String stable = "稳定性：" + isStable();
        return "【" + getClass().getSimpleName() + "】\n"
                + stable + " \t"
                + timeStr + " \t"
                + compareCountStr + "\t "
                + swapCountStr + "\n"
                + "------------------------------------------------------------------";
    }

    private String numberString(long number) {
        if (number < 10000) return "" + number;
        if (number < 100000000) return fmt.format(number / 10000.0) + "万";
        return fmt.format(number / 100000000.0) + "亿";
    }

    private boolean isStable() {
        if (this instanceof ShellSort) {
            return false;
        }
        if (this instanceof CountingSortV1) {
            return false;
        }
        if (this instanceof CountingSortV2) {
            return true;
        }
        if (this instanceof RadixSort) {
            return true;
        }
        Helper[] helpers = new Helper[10];
        for (int i = 0; i < helpers.length; i++) {
            helpers[i] = new Helper(88, i * 10);
        }
        sort((E[]) helpers);
        for (int i = 1; i < helpers.length; i++) {
            if (helpers[i].v2 - helpers[i - 1].v2 != 10) return false;
        }
        return true;
    }

    private static class Helper implements Comparable<Helper> {
        int v1;
        int v2;

        public Helper(int v1, int v2) {
            this.v1 = v1;
            this.v2 = v2;
        }

        @Override
        public int compareTo(Helper o) {
            return this.v1 - o.v1;
        }
    }
}
